package com.youpai.platform.chat.service.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.youpai.platform.chat.service.constant.ChatConstants;
import com.youpai.platform.chat.service.entity.dto.AckC2CMsgDTO;
import com.youpai.platform.chat.service.entity.dto.ChatMessageDTO;
import com.youpai.platform.chat.service.entity.po.ChatAccountPO;
import com.youpai.platform.chat.service.entity.po.ChatMessagePO;
import com.youpai.platform.chat.service.entity.po.ChatSessionPO;
import com.youpai.platform.chat.service.mapper.ChatAccountMapper;
import com.youpai.platform.chat.service.mapper.ChatMessageMapper;
import com.youpai.platform.chat.service.mapper.ChatSessionMapper;
import com.youpai.platform.chat.service.service.AckMsgService;
import com.youpai.platform.chat.service.service.ChatService;
import com.youpai.platform.chat.service.util.ChatMessageRedisKeyUtil;
import com.youpai.platform.chat.web.entity.dto.ClientC2CMsgDTO;
import com.youpai.platform.common.exceptions.BaseParamException;
import com.youpai.platform.user.service.entity.po.UserPO;
import com.youpai.platform.user.service.mapper.UserMapper;
import com.youpai.platform.vulture.service.entity.dto.ReceiveMessageDTO;
import com.youpai.platform.vulture.service.entity.dto.SendMessageDTO;
import com.youpai.platform.vulture.service.router.MessageRouterService;
import com.youpai.platform.chat.service.util.ChatUtils;
import com.youpai.platform.common.enums.ProcessCodeEnum;
import com.youpai.platform.common.exceptions.BaseBizException;
import com.youpai.platform.common.util.id.IdGenerator;
import com.youpai.platform.vulture.common.enums.ServerCommandEnum;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.concurrent.TimeUnit;

/**
 * @date: 2022/7/11 10:51
 * @author: Anna
 * @version: 1.0
 * @description: 聊天服务
 */
@Slf4j
@Component
public class ChatMsgServiceImpl extends AckMsgService implements ChatService {

    @Resource
    private MessageRouterService messageRouterService;
    @Resource
    private ChatAccountMapper chatAccountMapper;
    @Resource
    private ChatMessageMapper chatMessageMapper;
    @Resource
    private ChatSessionMapper chatSessionMapper;
    @Resource
    private UserMapper userMapper;
    @Resource
    private RedisTemplate<String, String> redisTemplate;

    @Override
    public void receiveC2CMessage(ReceiveMessageDTO messageDTO) {
        if (StringUtils.isBlank(messageDTO.getBody())) {
            throw new BaseParamException(ProcessCodeEnum.PARAM_ERROR);
        }
        ClientC2CMsgDTO clientC2CMsgDTO = JSONObject.parseObject(messageDTO.getBody(), ClientC2CMsgDTO.class);
        // 调整msgId
        clientC2CMsgDTO.setAppId(messageDTO.getAppId());
        Long msgId = IdGenerator.generateId();
        // 将msgId修改成serverMsgId
        clientC2CMsgDTO.setMsgId(msgId.toString());
        // 回复ack
        ackMsg(messageRouterService, buildAckC2CMsgDTO(clientC2CMsgDTO), clientC2CMsgDTO.getFromAccId());
        // 校验接收方、发送方
        checkAccount(clientC2CMsgDTO);
        // 处理会话
        saveChatSession(clientC2CMsgDTO);
        // 保存消息
        saveMessage(clientC2CMsgDTO);
        // 发送给接收方
        ChatMessageDTO chatMessageDTO = buildChatMessage(clientC2CMsgDTO);
        sendMessage(chatMessageDTO);
    }

    private AckC2CMsgDTO buildAckC2CMsgDTO(ClientC2CMsgDTO clientC2CMsgDTO) {
        AckC2CMsgDTO ackC2CMsgDTO = new AckC2CMsgDTO();
        ackC2CMsgDTO.setServerMsgId(Long.valueOf(clientC2CMsgDTO.getMsgId()))
                .setClientMsgId(clientC2CMsgDTO.getClientMsgId());
        return ackC2CMsgDTO;
    }

    private ChatMessageDTO buildChatMessage(ClientC2CMsgDTO clientC2CMsgDTO) {
        UserPO user = getFromUserInfo(ChatUtils.getUidByAccId(clientC2CMsgDTO.getFromAccId()));
        ChatMessageDTO chatMessageDTO = new ChatMessageDTO();
        chatMessageDTO.setPreMsgId(-1L)
                .setMsgId(Long.valueOf(clientC2CMsgDTO.getMsgId()))
                .setTimeStamp(System.currentTimeMillis())
                .setFromAccId(clientC2CMsgDTO.getFromAccId())
                .setFromUid(ChatUtils.getUidByAccId(clientC2CMsgDTO.getFromAccId()))
                .setFromNickName(user.getNickName())
                .setToAccId(clientC2CMsgDTO.getToAccId())
                .setToUid(ChatUtils.getUidByAccId(clientC2CMsgDTO.getToAccId()))
                .setFromAvatar(user.getAvatar())
                .setMsgType(clientC2CMsgDTO.getMsgType())
                .setContent(clientC2CMsgDTO.getContent())
                .setSessionId(clientC2CMsgDTO.getSessionId());
        return chatMessageDTO;
    }

    private UserPO getFromUserInfo(String uid) {
        LambdaQueryWrapper<UserPO> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(UserPO::getUid, uid);
        return userMapper.selectOne(wrapper);
    }

    private void saveChatSession(ClientC2CMsgDTO clientC2CMsgDTO) {
        String sessionId = ChatUtils.buildSessionId(clientC2CMsgDTO.getFromAccId(), clientC2CMsgDTO.getToAccId());
        ChatSessionPO chatSessionPO = new ChatSessionPO();
        chatSessionPO.setAccId(clientC2CMsgDTO.getFromAccId())
                .setLastMsgId(Long.valueOf(clientC2CMsgDTO.getMsgId()))
                .setSessionId(sessionId);
        doSaveChatSession(chatSessionPO);
        // 如果发送方和接收方不一致需要调用第二次
        if (!clientC2CMsgDTO.getFromAccId().equals(clientC2CMsgDTO.getToAccId())) {
            chatSessionPO.setAccId(clientC2CMsgDTO.getToAccId());
            doSaveChatSession(chatSessionPO);
        }
    }

    private void doSaveChatSession(ChatSessionPO chatSessionPO) {
        try {
            chatSessionMapper.insert(chatSessionPO);
        } catch (DuplicateKeyException duplicateKeyException) {
            ChatSessionPO newChatSessionPO = new ChatSessionPO();
            newChatSessionPO.setLastMsgId(chatSessionPO.getLastMsgId());
            LambdaQueryWrapper<ChatSessionPO> updateWrapper = new LambdaQueryWrapper<ChatSessionPO>()
                    .eq(ChatSessionPO::getSessionId, chatSessionPO.getSessionId());
            chatSessionMapper.update(newChatSessionPO, updateWrapper);
        }
    }

    private void sendMessage(ChatMessageDTO chatMessageDTO) {
        SendMessageDTO sendMessageDTO = buildSendMessageDTO(chatMessageDTO);
        messageRouterService.sendMessage(sendMessageDTO);
    }

    private SendMessageDTO buildSendMessageDTO(ChatMessageDTO chatMessageDTO) {
        SendMessageDTO sendMessageDTO = new SendMessageDTO();
        sendMessageDTO.setCmd(ServerCommandEnum.SRV_PUSH_MSG.cmd)
                .setBody(JSONObject.toJSONString(chatMessageDTO))
                .setToAccId(chatMessageDTO.getToAccId());
        return sendMessageDTO;
    }

    private void saveMessage(ClientC2CMsgDTO clientC2CMsgDTO) {
        ChatMessagePO chatMessagePO = new ChatMessagePO();
        chatMessagePO.setSessionId(ChatUtils.buildSessionId(clientC2CMsgDTO.getFromAccId(), clientC2CMsgDTO.getToAccId()))
                .setFromAccId(clientC2CMsgDTO.getFromAccId())
                .setMessageId(Long.valueOf(clientC2CMsgDTO.getMsgId()))
                .setTimeStamp(System.currentTimeMillis())
                .setMsgType(clientC2CMsgDTO.getMsgType())
                .setToAccId(clientC2CMsgDTO.getToAccId())
                .setContent(clientC2CMsgDTO.getContent());
        chatMessageMapper.insertChatMessagePO(chatMessagePO);
        String recallMessageKey = ChatMessageRedisKeyUtil.getRecallMessageCacheKey(clientC2CMsgDTO.getMsgId());
        redisTemplate.opsForValue().set(recallMessageKey, "1");
        redisTemplate.opsForValue().getOperations().expire(recallMessageKey, ChatConstants.FIVE_MIN_SECOND, TimeUnit.SECONDS);
    }

    private void checkAccount(ClientC2CMsgDTO clientC2CMsgDTO) {
        String fromAccId = clientC2CMsgDTO.getFromAccId();
        String toAccId = clientC2CMsgDTO.getToAccId();
        LambdaQueryWrapper<ChatAccountPO> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(ChatAccountPO::getAppId, clientC2CMsgDTO.getAppId())
                .eq(ChatAccountPO::getAccId, fromAccId);
        ChatAccountPO chatAccountPO = chatAccountMapper.selectOne(queryWrapper);
        if (null == chatAccountPO) {
            log.error("发送方聊天账号未找到 - {}", fromAccId);
            throw new BaseBizException(ProcessCodeEnum.TOKEN_FAILURE);
        }
        queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(ChatAccountPO::getAppId, clientC2CMsgDTO.getAppId())
                .eq(ChatAccountPO::getAccId, toAccId);
        chatAccountPO = chatAccountMapper.selectOne(queryWrapper);
        if (null == chatAccountPO) {
            log.error("接收方聊天账号未找到 - {}", toAccId);
            throw new BaseBizException(ProcessCodeEnum.TOKEN_FAILURE);
        }
    }
}
